package work.smallRulesGui;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.TreeMap;

import javax.swing.DefaultListModel;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class SmallRuleGui {
	public static final int MAX_SIZE = 2000, STANDARD_SIZE = 1500;
	public static DefaultListModel<String> useCasesModel, flowsModel, handlersModel;
	public static TreeMap<String, String> useCasesMap = new TreeMap<String, String>(),
																				flowsMap = new TreeMap<String, String>(), 
																				handlersMap = new TreeMap<String, String>();
	public static HashMap<String, ArrayList<String>> actionsMap = new HashMap<String, ArrayList<String>>();
	public static HashMap<String, Integer> actionIndexes = new HashMap<String, Integer>();
	public static String textBefore = null;
	public static JTabbedPane tabbedPane = new JTabbedPane();
	public static JList<String> handlersList = new JList<String>(), flowsList = new JList<String>();
	public static JFileChooser openFileChooser = new JFileChooser(), saveFileChooser = new JFileChooser();
	public static String selectedHandler = null, selectedFlow = null;
	public static JLabel statusLabel = new JLabel();
	public static ArrayList<String> pathStack = new ArrayList<String>();
	public static int pathIndex = 0;
	public static JFrame frame = new JFrame("Small rulegui");
	public static void main(String[] args) {
		JSplitPane useCasesSplitPane = new JSplitPane();
		useCasesModel = new DefaultListModel<String>();
		JList<String> useCasesList = new JList<String>();
		useCasesList.setModel(useCasesModel);
		useCasesSplitPane.setLeftComponent(useCasesList);
		JTextArea useCaseTextArea = new JTextArea();
		useCasesSplitPane.setRightComponent(useCaseTextArea);
		useCasesSplitPane.setDividerLocation(250);
		tabbedPane.addTab("Use cases", useCasesSplitPane);

		JSplitPane flowsSplitPane = new JSplitPane();
		flowsModel = new DefaultListModel<String>();
		flowsList.setModel(flowsModel);
		flowsSplitPane.setLeftComponent(new JScrollPane(flowsList));
		final JTextArea flowTextArea = new JTextArea();
		flowsSplitPane.setRightComponent(new JScrollPane(flowTextArea));
		flowsSplitPane.setDividerLocation(250);
		tabbedPane.addTab("Flows", flowsSplitPane);

		JSplitPane handlersSplitPane = new JSplitPane();
		handlersModel = new DefaultListModel<String>();
		handlersList.setModel(handlersModel);
		handlersSplitPane.setLeftComponent(new JScrollPane(handlersList));
		final JEditorPane handlerTextArea = new JEditorPane();
		handlerTextArea.setFont(new Font("Verdana", Font.PLAIN, 14));
		handlersSplitPane.setRightComponent(new JScrollPane(handlerTextArea));
		handlersSplitPane.setDividerLocation(250);
		tabbedPane.addTab("Handlers", handlersSplitPane);

		openFileChooser.setSelectedFile(new File("f:\\eclipse\\workspace\\cr48Old\\bin"));
		saveFileChooser.setSelectedFile(new File("f:\\eclipse\\workspace\\cr48Old\\bin"));

		handlersList.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				if(e.getValueIsAdjusting()) {
					return;
				}
				if(selectedHandler != null) {
					String val = handlerTextArea.getText();
					handlersMap.put(selectedHandler, val);
				}
				if(handlersList.getSelectedValue() == null) {
					return;
				}
				selectedHandler = handlersList.getSelectedValue().toString();
				textBefore = handlersMap.get(selectedHandler);
				handlerTextArea.setText(textBefore);
				handlerTextArea.select(0, 0);
				String pathStr = "h" + selectedHandler;
				addStrToPathStack(pathStr);
				handlerTextArea.requestFocus();
			}
		});
		flowsList.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				if(e.getValueIsAdjusting()) {
					return;
				}
				if(selectedFlow != null) {
					String val = flowTextArea.getText();
					flowsMap.put(selectedFlow, val);
				}
				if(flowsList.getSelectedValue() == null) {
					return;
				}
				selectedFlow = flowsList.getSelectedValue().toString();
				textBefore = flowsMap.get(selectedFlow);
				flowTextArea.setText(textBefore);
				String pathStr = "f" + selectedFlow;
				addStrToPathStack(pathStr);
				flowTextArea.requestFocus();
			}
		});
		KeyListener kl = new KeyAdapter() {
			private String lastFindWord = "";
			@Override
			public void keyPressed(KeyEvent e) {
				// keep track for undo/redo actions
				if(e.getComponent() instanceof JTextArea) {
					JTextArea area = (JTextArea)e.getComponent();
					if(textBefore != null && !textBefore.equals(area.getText())) {
						addAction(e);
					}
					textBefore = area.getText();
				}
				// delete flow/handler option
				if(e.getKeyCode() == KeyEvent.VK_DELETE) {
					if(flowsList.hasFocus()) {
						String flowName = flowsList.getSelectedValue().toString();
						flowsMap.remove(flowName);
						flowsList.setSelectedIndex(1);
						flowsModel.removeElement(flowName);
						flowsList.repaint();
					}
				}
				// find next
				if(e.getKeyCode() == KeyEvent.VK_F3 || (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_K)) {
					find(lastFindWord, e, e.isShiftDown());
				}
				if(e.isControlDown()) {
					// open option
					if(e.getKeyCode() == KeyEvent.VK_O) {
						int res = openFileChooser.showOpenDialog(frame);
						if(res == JFileChooser.CANCEL_OPTION) {
							return;
						}
						final File fl = openFileChooser.getSelectedFile();
						new Thread() {
							public void run() {
								flowsModel.clear();
								flowsMap.clear();
								handlersMap.clear();
								handlersModel.clear();
								pathStack.clear();
								pathIndex = 0;
								IOUtil.loadFile(fl);
								flowsList.repaint();
								handlersList.repaint();
								frame.repaint();
							}
						}.start();
					}
					// back option
					if(e.getKeyCode() == KeyEvent.VK_B) {
						if(pathIndex <= 1) {
							return;
						}
						pathIndex--;
						String last = pathStack.get(pathIndex - 1);
						if(last.startsWith("f")) {
							tabbedPane.setSelectedIndex(1);
							flowsList.setSelectedValue(last.substring(1), true);
						} else if(last.startsWith("h")) {
							tabbedPane.setSelectedIndex(2);
							handlersList.setSelectedValue(last.substring(1), true);
						}
					}
					// next option
					if(e.getKeyCode() == KeyEvent.VK_N) {
						if(pathIndex >= pathStack.size()) {
							return;
						}
						String last = pathStack.get(pathIndex);
						pathIndex++;
						if(last.startsWith("f")) {
							tabbedPane.setSelectedIndex(1);
							flowsList.setSelectedValue(last.substring(1), true);
						} else if(last.startsWith("h")) {
							tabbedPane.setSelectedIndex(2);
							handlersList.setSelectedValue(last.substring(1), true);
						}
					}
					// save option
					if(e.getKeyCode() == KeyEvent.VK_S) {
						int res = saveFileChooser.showSaveDialog(frame);
						if(res == JFileChooser.CANCEL_OPTION) {
							return;
						}
						final File fl = saveFileChooser.getSelectedFile();
						String val = flowTextArea.getText();
						flowsMap.put(selectedFlow, val);
						val = handlerTextArea.getText();
						handlersMap.put(selectedHandler, val);
						IOUtil.saveFile(fl);
					}
					// new flow/handler option
					if(e.getKeyCode() == KeyEvent.VK_W) {
						if(tabbedPane.getSelectedIndex() == 1) {
							int x = 1;
							while(flowsMap.containsKey("Flow" + x)) {
								x++;
							}
							String newFlowName = "Flow" + x;
							flowsMap.put(newFlowName, "");
							flowsModel.addElement(newFlowName);
							IOUtil.flowsOrder.add(newFlowName);
							flowsList.repaint();
							flowsList.setSelectedValue(newFlowName, true);
						}
						if(tabbedPane.getSelectedIndex() == 2) {
							int x = 1;
							while(handlersMap.containsKey("Handler" + x)) {
								x++;
							}
							String newHandlerName = "Handler" + x;
							handlersMap.put(newHandlerName, "");
							handlersModel.addElement(newHandlerName);
							IOUtil.handlersOrder.add(newHandlerName);
							handlersList.repaint();
							handlersList.setSelectedValue(newHandlerName, true);
						}
					}
					// find option
					if(e.getKeyCode() == KeyEvent.VK_F) {
						String word = JOptionPane.showInputDialog("Find", lastFindWord);
						lastFindWord = word;
						boolean found = find(word, e, false);
						if(!found) {

						}
					}
					// undo option
					if(e.getKeyCode() == KeyEvent.VK_Z) {
						undo(e);
					}
					// redo option
					if(e.getKeyCode() == KeyEvent.VK_Y) {
						redo(e);
					}
				}
			}
			private boolean find(String word, KeyEvent e, boolean backward) {
				if(flowsMap != null && tabbedPane.getSelectedIndex() == 1 && flowsMap.containsKey(word)) {
					flowsList.setSelectedValue(word, true);
					return true;
				}
				if(handlersMap != null && tabbedPane.getSelectedIndex() == 2 && handlersMap.containsKey(word)) {
					handlersList.setSelectedValue(word, true);
					return true;
				}
				if(e.getComponent() instanceof JTextArea) {
					JTextArea area = (JTextArea)e.getComponent();
					String text = area.getText();
					int cp = area.getCaretPosition();
					int ind = text.substring(cp).indexOf(word);
					if(backward) {
						ind = cp > 0 ? text.substring(0, cp - 1).lastIndexOf(word) : -1;
					}
					if(ind == -1) {
						if(backward) {
							ind = text.lastIndexOf(word);
						} else {
							ind = text.indexOf(word);
						}
						if(ind != -1) {
							area.select(ind, ind + word.length());
							return true;
						}
					} else {
						if(backward) {
							area.select(ind, ind + word.length());
						} else {
							area.select(ind + cp, ind + cp + word.length());
						}
						return true;
					}
				}
				
				boolean passedCHandler = handlersList.getSelectedValue() == null;
				for(String hName : handlersMap.keySet()) {
					if(!passedCHandler) {
						passedCHandler = hName.equals(handlersList.getSelectedValue());
						continue;
					}
					String text = handlersMap.get(hName);
					if(text.indexOf(word) != -1) {
						tabbedPane.setSelectedIndex(2);
						handlersList.setSelectedValue(hName, false);
						return true;
					}
				}
				
				passedCHandler = flowsList.getSelectedValue() == null;
				for(String fName : flowsMap.keySet()) {
					if(!passedCHandler) {
						passedCHandler = fName.equals(flowsList.getSelectedValue());
						continue;
					}
					String text = flowsMap.get(fName);
					if(text.indexOf(word) != -1) {
						tabbedPane.setSelectedIndex(1);
						flowsList.setSelectedValue(fName, false);
						return true;
					}
				}
				
				return false;
			}
		};
		MouseListener ml = new MouseAdapter() {
			@Override
			public void mousePressed(MouseEvent e) {
				try {
					if(e.isControlDown() && e.getComponent() instanceof JTextArea) {
						JTextArea area = (JTextArea)e.getComponent();
						int ipos = area.viewToModel(e.getPoint());
						String text = " " + area.getText() + " \n";
						
						String strBefore = text.substring(0, ipos);
						String strAfter = text.substring(ipos);
						int ind1 = Math.max(strBefore.lastIndexOf(" "), strBefore.lastIndexOf("\n"));
						int ind2 = Math.min(strAfter.indexOf(" "), strAfter.indexOf("\n"));
						String word = text.substring(ind1 + 1, ipos + ind2).replaceAll("\n", "");
						boolean isHandler = false;
						if(word.indexOf("(") != -1) {
							word = word.substring(0, word.indexOf("("));
							isHandler = true;
						}
						if(flowsMap.containsKey(word)) {
							flowsList.setSelectedValue(word, true);
							return;
						}
						if(handlersMap.containsKey(word)) {
							tabbedPane.setSelectedIndex(2);
							handlersList.setSelectedValue(word, true);
							return;
						}
						if(isHandler) {
							handlersMap.put(word, "");
							handlersModel.addElement(word);
							IOUtil.handlersOrder.add(word);
							handlersList.repaint();
							handlersList.setSelectedValue(word, true);
							actionsMap.put("h" + word, new ArrayList<String>());
						} else {
							flowsMap.put(word, "");
							flowsModel.addElement(word);
							IOUtil.flowsOrder.add(word);
							flowsList.repaint();
							flowsList.setSelectedValue(word, true);
							actionsMap.put("f" + word, new ArrayList<String>());
						}
					}
				}catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		};
		frame.addKeyListener(kl);
		tabbedPane.addKeyListener(kl);
		useCasesList.addKeyListener(kl);
		flowsList.addKeyListener(kl);
		handlersList.addKeyListener(kl);
		useCaseTextArea.addKeyListener(kl);
		flowTextArea.addKeyListener(kl);
		handlerTextArea.addKeyListener(kl);
		flowTextArea.addMouseListener(ml);
		
		frame.getContentPane().add(tabbedPane, BorderLayout.CENTER);
		frame.getContentPane().add(new JPanel().add(statusLabel), BorderLayout.SOUTH);
		frame.setSize(1200, 700);
		frame.setVisible(true);
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	}
	
	static void addStrToPathStack(String pathStr) {
		if(pathIndex == 0 || !pathStack.get(pathIndex - 1).equals(pathStr)) {
			if(pathStack.size() <= pathIndex) {
				pathStack.add(pathStr);
			} else {
				pathStack.set(pathIndex, pathStr);
			}
			pathIndex++;
		}
	}
	
	static void addAction(KeyEvent e) {
		JTextArea area = (JTextArea)e.getComponent();
		int areaInd = area.getCaretPosition();
		String text = areaInd + "|" + area.getText();
		String key = null;

		if(tabbedPane.getSelectedIndex() == 2 && handlersList.getSelectedValue() != null) {
			key = "h" + handlersList.getSelectedValue();
		} else if(tabbedPane.getSelectedIndex() == 1 && flowsList.getSelectedValue() != null) {
			key = "f" + flowsList.getSelectedValue();
		} else {
			return;
		}
		
		if(!actionIndexes.containsKey(key)) {
			actionIndexes.put(key, 1);
		}
		List<String> list = actionsMap.get(key);

		int ind = actionIndexes.get(key);
		if(ind > 0 && ind - 1 < list.size() && text.equals(list.get(ind - 1))) {
			return;
		}
		if(ind < list.size()) {
			list.set(ind, text);
			int tmp = ind + 1;
			while(tmp < list.size()) {
				list.remove(tmp);
				tmp++;
			}
		} else {
			list.add(text);
			if(list.size() > MAX_SIZE) {
				while(list.size() > STANDARD_SIZE) {
					list.remove(0);
				}
				ind = list.size() - 1;
			}
		}
		actionIndexes.put(key, ind + 1);
	}
	static void undo(KeyEvent e) {
		if(!(e.getComponent() instanceof JTextArea)) {
			return;
		}
		String key = null;

		if(tabbedPane.getSelectedIndex() == 2 && handlersList.getSelectedValue() != null) {
			key = "h" + handlersList.getSelectedValue();
		} else if(tabbedPane.getSelectedIndex() == 1 && flowsList.getSelectedValue() != null) {
			key = "f" + flowsList.getSelectedValue();
		} else {
			return;
		}
		if(!actionIndexes.containsKey(key)) {
			return;
		}
		
		int ind = actionIndexes.get(key) - 2;
		if(ind < 0) {
			return;
		}
		String text = actionsMap.get(key).get(ind);
		actionIndexes.put(key, ind + 1);
		
		JTextArea area = (JTextArea)e.getComponent();
		ind = text.indexOf("|");
		area.setText(text.substring(ind + 1));
		area.setCaretPosition(Integer.parseInt(text.substring(0, ind)));
	}
	static void redo(KeyEvent e) {
		if(!(e.getComponent() instanceof JTextArea)) {
			return;
		}
		String key = null;

		if(tabbedPane.getSelectedIndex() == 2 && handlersList.getSelectedValue() != null) {
			key = "h" + handlersList.getSelectedValue();
		} else if(tabbedPane.getSelectedIndex() == 1 && flowsList.getSelectedValue() != null) {
			key = "f" + flowsList.getSelectedValue();
		} else {
			return;
		}
		if(!actionIndexes.containsKey(key)) {
			return;
		}
		
		int ind = actionIndexes.get(key);
		if(ind >= actionsMap.get(key).size()) {
			return;
		}
		String text = actionsMap.get(key).get(ind);
		actionIndexes.put(key, ind + 1);
		
		JTextArea area = (JTextArea)e.getComponent();
		ind = text.indexOf("|");
		area.setText(text.substring(ind + 1));
		area.setCaretPosition(Integer.parseInt(text.substring(0, ind)));
	}
}
